home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 2002 November / SGI Freeware 2002 November - Disc 1.iso / dist / fw_exmh.idb / usr / freeware / lib / exmh-2.5 / faces.tcl.z / faces.tcl
Text File  |  2002-07-08  |  14KB  |  573 lines

  1. # faces.tcl
  2. #
  3. # facesaver support (bitmap display of who sent a message).
  4. #
  5. # Copyright (c) 1993 Xerox Corporation.
  6. # Use and copying of this software and preparation of derivative works based
  7. # upon this software are permitted. Any distribution of this software or
  8. # derivative works must comply with all applicable United States export
  9. # control laws. This software is made available AS IS, and Xerox Corporation
  10. # makes no warranty about the software, its performance or its conformity to
  11. # any specification.
  12.  
  13. #### Faces support
  14.  
  15. set faces(debug) 0
  16. proc Dputs args { global faces; if $faces(debug) {puts $args} }
  17. set faces(timing) 0
  18. proc Tputs args { global faces; if $faces(timing) {puts $args} }
  19.  
  20. # Compute faces search path
  21. proc Face_SetPath {} {
  22.     global faces env faceCache
  23.  
  24.     catch {unset faceCache}
  25.  
  26.     if ![info exists faces(sets)] {
  27.     if [info exists faces(set)] {
  28.         # backwards compatibility with old "exmh" script
  29.         set faces(set,user) $faces(set)
  30.         set faces(set,unknown) $faces(set)
  31.         set faces(set,news) $faces(set)
  32.     }
  33.     set faces(sets) {user unknown}
  34.     }
  35.  
  36.     # tail component for each set
  37.     set faces(name,user) {$user}
  38.     set faces(name,unknown) unknown
  39.     set faces(name,news) unknown
  40.  
  41.     set faces(defaultDomain) [string tolower \
  42.     [string trim $faces(defaultDomain) ". "]]
  43.     # Build search path
  44.     foreach set $faces(sets) {
  45.     set faces(path,$set) {}
  46.     }
  47.     set faces(path,news) {}
  48.     if [info exists env(FACEPATH)] {
  49.     set faces(base) ""
  50.     foreach dir [split $env(FACEPATH) :] {
  51.         foreach set $faces(sets) {
  52.         if ![file isdirectory $dir] continue
  53.         if {[lsearch -exact $faces(set,$set) [file tail $dir]] >= 0} {
  54.             FaceAddPath $set $dir
  55.         } else {
  56.             FaceAddPath user $dir
  57.             FaceAddPath unknown $dir
  58.             FaceAddPath news $dir
  59.         }
  60.         }
  61.     }
  62.     } else {
  63.     set faces(base) $faces(dir)/
  64.     foreach set $faces(sets) {
  65.         foreach dir $faces(set,$set) {
  66.         if ![file isdirectory $faces(base)$dir] continue
  67.         FaceAddPath $set $dir
  68.         }
  69.     }
  70.     if [info exists faces(set,news)] {
  71.         foreach dir $faces(set,news) {
  72.         if ![file isdirectory $faces(base)$dir] continue
  73.         FaceAddPath news $dir
  74.         }
  75.     }
  76.     }
  77. }
  78. proc FaceAddPath {set dir} {
  79.     global faces
  80.     lappend faces(path,$set) $dir
  81.     set mmap [file exists $faces(base)$dir/machine.tab]
  82.     set pmap [file exists $faces(base)$dir/people.tab]
  83.     set faces(map,$dir) [expr ($mmap<<1) + $pmap]
  84.     if [file isdirectory $faces(base)$dir/MISC] {
  85.     lappend faces(path,$set) $dir/MISC
  86.     set faces(map,$dir/MISC) 0
  87.     }
  88. }
  89.  
  90.  
  91. proc Face_Show { fromwho {xface {}} {ximageurl {}} {newsgrps {}} } {
  92.     global faces faceCache failedURLs exmh
  93.  
  94.     set xfaceAvail 0
  95.     set ximageurlAvail 0
  96.  
  97.     # Don't do any of this if we're on a slow display
  98.     if {!$exmh(slowDispShowFaces)} {
  99.       return 0
  100.     }
  101.  
  102.     Face_Delete
  103.  
  104.     # Honor X-Face even if faces is disabled
  105.     if {$faces(xFaceEnabled) && \
  106.     [string compare "" $xface] && \
  107.     [string compare "" $faces(xfaceProg)]} {
  108.  
  109.     if {$faces(rowEnabled) && $faces(defer)} {
  110.         DeferWork faces(work) [list FaceXFace $xface [FaceAlloc]]
  111.     } elseif {[FaceXFace $xface] && !$faces(rowEnabled)} {
  112.         set xfaceAvail 1
  113.     }
  114.     }
  115.  
  116.     # Honor X-Image-URL even if X-Face was displayed or the faces are
  117.     # disabled
  118.     if {$faces(xImageUrl) && [string compare "" $ximageurl]} {
  119.     if {![info exists failedURLs]
  120.         || ([info exists failedURLs]
  121.         && [lsearch $failedURLs $ximageurl] == -1)} {
  122.          if {$faces(rowEnabled) && $faces(defer)} {
  123.          DeferWork faces(work) \
  124.             [list UrlDisplayFace $ximageurl [FaceAlloc]]
  125.          } elseif {[UrlDisplayFace $ximageurl [FaceAlloc]]
  126.               && !$faces(rowEnabled)} {
  127.         set ximageurlAvail 1
  128.         }
  129.     }
  130.     }
  131.  
  132.     if {$xfaceAvail || $ximageurlAvail} {
  133.     return 1
  134.     }
  135.  
  136.     if {$faces(enabled!) || !$faces(enabled)} {
  137.     return 0
  138.     }
  139.  
  140.     # Check for cached lookup result
  141.     if [info exists faceCache($fromwho,$newsgrps)] {
  142.     if [Face_ShowFace $faceCache($fromwho,$newsgrps)] {
  143.         return 1
  144.     }
  145.     unset faceCache($fromwho,$newsgrps)
  146.     Face_Delete
  147.     }
  148.  
  149.     set msg [Exmh_OldStatus]
  150.     Exmh_Status "Looking up face of $fromwho ..."
  151.  
  152.     set parts [string tolower [split $fromwho @]]
  153.     set user [lindex $parts 0]
  154.     set machine [lindex $parts 1]
  155.     if {[string length $machine] == 0} {
  156.     set machine [string tolower $faces(defaultDomain)]
  157.     } elseif {[string first . $machine] == -1} {
  158.       append machine . $faces(defaultDomain)
  159.     }
  160.  
  161.     set from [split $machine .]
  162.     set pathlist [FacePathlist $from]
  163.  
  164. #Exmh_Debug \n$user ==> $pathlist
  165.  
  166.     set pathlistngfull {}
  167.     if {[string compare "" $newsgrps]} {
  168.     set newsgrplist [string tolower [split $newsgrps ,]]
  169.     foreach ng $newsgrplist {
  170.         set ngparts [split $ng .]
  171.         set pathlistng [FacePathNGlist $ngparts]
  172.         set pathlistngfull [concat $pathlistng $pathlistngfull]
  173.     }
  174.     }
  175.  
  176.     # Loop through Face path
  177. #Tputs lookup: [time {
  178.     set matches {}
  179.     foreach set $faces(sets) {
  180.     eval set tail $faces(name,$set)
  181.         foreach dir $faces(path,$set) {
  182.         set name $tail
  183.         set map {}
  184.         if $faces(map,$dir) {
  185.         if {$faces(map,$dir) & 2} {
  186.             set map [FacePathlist [split \
  187.                 [FaceMap $dir/machine.tab $machine] .]]
  188. #            Exmh_Debug $machine => $map
  189.         }
  190.         if {$faces(map,$dir) & 1} {
  191.             set x [FaceMap $dir/people.tab $machine/$name]
  192. #            Exmh_Debug $machine/$name =>  $x
  193.             if [string compare "" $x] {
  194.             set name $x
  195.             }
  196.         }
  197.         }
  198.         foreach part [concat $map $pathlist] {
  199.             if {([string match unknown* $dir] || [string match misc* $dir])
  200.              && [llength $matches]} {
  201.             break
  202.         }
  203.         set path $dir/$part/$name
  204. #        Exmh_Debug $path
  205.         # skip non-existent directories
  206.         if ![file exists $faces(base)$path] continue
  207.  
  208.         foreach suf $faces(suffix) {
  209.             if [file exists $faces(base)$path/face.$suf] {
  210.             lappend matches $path/face.$suf
  211.             break
  212.             }
  213.         }
  214.         }
  215.     }
  216.     }
  217. #   }]
  218.     eval set tail $faces(name,news)
  219.     foreach dir $faces(path,news) {
  220.     set name $tail
  221.     set map {}
  222.     foreach part [concat $map $pathlistngfull] {
  223. #        if {([string match unknown* $dir] || [string match misc* $dir])
  224. #         && [llength $matches]} {
  225. #        break
  226. #        }
  227.         set path $dir/$part/$name
  228. #        Exmh_Debug $path
  229.         # skip non-existent directories
  230.         if ![file exists $faces(base)$path] continue
  231.  
  232.         foreach suf $faces(suffix) {
  233.         if [file exists $faces(base)$path/face.$suf] {
  234.             lappend matches $path/face.$suf
  235.             break
  236.             }
  237.         }
  238.     }
  239.     }
  240.  
  241. #    Exmh_Debug Faces matches $matches
  242.  
  243.     if !$faces(rowEnabled) {
  244.     foreach face $matches {
  245.         if [Face_ShowFile $face] {
  246.         set faceCache($fromwho,$newsgrps) $face
  247.         Exmh_Status $msg
  248.         return 1
  249.         }
  250.     }
  251.     # braces around cmdsubst NECESSARY!
  252.     } elseif {[Face_ShowFace $matches]} {
  253.     set faceCache($fromwho,$newsgrps) $matches
  254.     Exmh_Status $msg
  255.     return 1
  256.     }
  257.  
  258.     if [llength $matches] {
  259.     Exmh_Status "(no working face found)"
  260.     } else {
  261.     Exmh_Status "(no face found)"
  262.     }
  263.     return 0
  264. }
  265.  
  266. proc FacePathlist { from } {
  267.     set path {}
  268.     set prefix {}
  269.     set pathlist {}
  270.     for {set i [expr [llength $from]-1]} {$i>=0} {incr i -1} {
  271.     append path $prefix [lindex $from $i]
  272.     set prefix /
  273.     set pathlist [concat $path $pathlist]
  274.     }
  275.     lappend pathlist {}
  276.     return $pathlist
  277. }
  278.  
  279. proc FacePathNGlist { ng } {
  280.     set path {}
  281.     set prefix {}
  282.     set pathlist {}
  283.     for {set i 0} {$i <= [expr [llength $ng]-1]} {incr i 1} {
  284.     append path $prefix [lindex $ng $i]
  285.     set prefix /
  286.     set pathlist [concat $path $pathlist]
  287.     }
  288.     lappend pathlist {}
  289.     return $pathlist
  290. }
  291.  
  292. proc Face_Delete {} {
  293.     global faces
  294.  
  295.     if [info exists faces(work)] {
  296.     DeferWorkCancel faces(work)
  297.     }
  298.  
  299.     for {set f $faces(avail)} {$f > 0} {incr f -1} {
  300.     catch {
  301.         set image [$faces(frame).l$f cget -image]
  302.         if [string compare "" $image] {
  303.         $faces(frame).l$f config -image {}
  304.         image delete $image
  305.         }
  306.     }
  307.     $faces(frame).l$f config -bitmap {}
  308.     if {$faces(rowEnabled) && [info exists faces(rowbg)]} {
  309.         $faces(frame).l$f config -bg $faces(rowbg)
  310.     }
  311.     }
  312.     set faces(avail) 0
  313.  
  314.     if !$faces(rowEnabled) {
  315.     raise $faces(default)
  316.     }
  317. }
  318.  
  319. proc FaceAlloc {} {
  320.     global faces
  321.  
  322.     set new 0
  323.     if {!$faces(rowEnabled) && $faces(avail)} {
  324.         catch {
  325.             set image [$faces(frame).l$faces(avail) cget -image]
  326.             if [string compare "" $image] {
  327.                 $faces(frame).l$faces(avail) config -image {}
  328.                 image delete $image
  329.             }
  330.         }
  331.     incr faces(avail) -1    ;# make us alloc same label
  332.     }
  333.     if {$faces(avail) == $faces(alloc)} {
  334.     Widget_Label $faces(frame) l[incr faces(alloc)] {left fill}
  335.         set new 1
  336.     }
  337.     set label $faces(frame).l[incr faces(avail)]
  338.  
  339.     if !$faces(rowEnabled) {
  340.     if $new {        ;# once ever
  341.         pack forget $label
  342.         place $label -in $faces(default)
  343.     }
  344.     } elseif !$new {
  345.     $label config -bg $faces(facebg)
  346.     }
  347.  
  348.     return $label
  349. }
  350. proc Face_BusyParent {} {
  351.     global faces
  352.     return $faces(frame)
  353. }
  354. proc Face_BusyPlace {busy} {
  355.     global faces
  356.     place $busy -in $faces(frame) -anchor c -relx 0.5 -rely 0.5
  357.     raise $busy
  358.     update idletasks
  359. }
  360. proc Face_BusyDestroy {busy} {
  361.     global faces
  362.     catch {
  363.     destroy $busy
  364.     # This hack forces the underlying labels to redisplay immediatly
  365.     $faces(default) config -fg [lindex [$faces(default) config -fg] 4]
  366.     $faces(frame).l1 config -fg [lindex [$faces(frame).l1 config -fg] 4]
  367.     }
  368. }
  369. proc Face_ShowFace facelist {
  370.     foreach face $facelist {
  371.     if ![FaceShowFile $face [FaceAlloc]] {
  372.         return 0
  373.     }
  374.     }
  375.     return 1
  376. }
  377. proc Face_ShowFile facefile {
  378.     set pane [FaceAlloc]
  379.     if ![FaceShowFile $facefile $pane] {
  380.     $pane config -bitmap error
  381.     return 0
  382.     }
  383.     return 1
  384. }
  385. proc FaceShowFile {facefile pane} {
  386.     global faces
  387.  
  388.     if ![string match /* $facefile] {
  389.     set facefile $faces(base)$facefile
  390.     }
  391.     switch -- [file extension $facefile] {
  392.     .ppm - .pgm - .pbm - .gif - .xpm {
  393.         if [catch {
  394. # Tputs image create: [time {
  395.         set image [image create photo -file $facefile -palette $faces(palette)]
  396. # }]
  397.         if $faces(defer) {
  398.             DeferWork faces(work) [list $pane config -image $image] \
  399.                   [list image delete $image]
  400.  
  401.         } else {
  402. # Tputs image display: [time {
  403.             $pane config -image $image
  404. # }]
  405.         }
  406.         } id] {
  407.         Exmh_Debug $id
  408.         return 0
  409.         }
  410.     }
  411.     .xbm {
  412.         if [catch {
  413.         $pane config -bitmap @$facefile
  414.         } id] {
  415.         Exmh_Debug $id
  416.         return 0
  417.         }
  418.     }
  419.     }
  420.     if !$faces(rowEnabled) {
  421.         raise $pane
  422.     }
  423.     return 1
  424. }
  425.  
  426. proc FaceXFace { xface {pane {}}} {
  427.     global faces
  428.     Exmh_Status "$faces(xfaceProg)" red
  429. # Tputs decode x-face: [time {
  430.     if [catch {open "| $faces(xfaceProg) > [Env_Tmp]/FACE.[pid].xbm" w} fid] {
  431.     Exmh_Status $fid error
  432.     return 0
  433.     } else {
  434.     Exmh_Status "$faces(xfaceProg)"
  435.     }
  436.     puts $fid $xface
  437.     if [catch {close $fid} err] {
  438.     Exmh_Status $err error
  439.     return 0
  440.     }
  441. # }]
  442.     if [string match "" $pane] {
  443.     set pane [FaceAlloc]
  444.     }
  445. # Tputs show x-face: [time {
  446.     set ret [FaceShowFile [Env_Tmp]/FACE.[pid].xbm $pane]
  447. # }]
  448.     File_Delete [Env_Tmp]/FACE.[pid].xbm
  449.     Exmh_Status ok
  450.     return $ret
  451. }
  452.  
  453. #
  454. # Hook for button in faces area
  455. #
  456. proc Faces_Button {{cmd ""} {label ""} {pack {left fill}}} {
  457.     global faces
  458.     catch {destroy $faces(button)}
  459.     set faces(button) [Widget_AddBut $faces(frame) b $label $cmd $pack]
  460.     $faces(button) config -padx 0 -pady 0
  461.     pack $faces(button) -after $faces(default)
  462.     return $faces(button)
  463. }
  464. proc Faces_ClearButton {} {
  465.     global faces
  466.     catch {destroy $faces(button)}
  467. }
  468.  
  469.  
  470. # Faces information used to be administered by a pair of ASCII files
  471. # in the faces directory that associate related machines and faces.
  472. # EXMH still supports this mechanism, although it's use is discouraged.
  473. # The machine table machine.tab attaches machines to communities; the line
  474. #    stard=sunaus
  475. # puts the machine stard in community sunaus.  The machine
  476. # table may be used to alias entire communities; the line
  477. #    wseng.sun.com=eng.sun.com
  478. # will cause the wseng.sun.com domain to be mapped to the
  479. # eng.sun.com community.  The people table associates a
  480. # community/alias pair, with a real username.
  481. #    sunaus/rburridge=richb
  482. # causes the alias rburridge to be translated into the real
  483. # username richb for the community sunaus
  484.  
  485. proc FaceMachine {dir machine} {
  486.     global faces
  487.     if $faces(mapsEnabled) {
  488.     set map [FaceMap $dir/machine.tab $machine]
  489.     if [string compare "" $map] {
  490.         return $map
  491.     }
  492.     }
  493.     return $machine
  494. }
  495. proc FacePeople {dir machine people} {
  496.     global faces
  497.     if $faces(mapsEnabled) {
  498.     set map [FaceMap $dir/people.tab $machine/$people]
  499.     switch -- [llength $map] {
  500.     0    {}
  501.     1    {return [list $machine $map]}
  502.     default    {return $map}
  503.     }
  504.     }
  505.     return [list $machine $people]
  506. }
  507. proc FaceMap {file item} {
  508.     global faceMap faces
  509.     if [info exists faceMap($file,$item)] {
  510.     return $faceMap($file,$item)
  511.     }
  512.     if { [info exists faceMap(modtime,$file)] &&
  513.     ([file mtime $faces(base)$file]  <= $faceMap(modtime,$file)) } {
  514.     return {}
  515.     }
  516. #    Exmh_Debug FaceMap $file $item
  517.     if ![catch {open $faces(base)$file} in] {
  518.     set faceMap(modtime,$file) [file mtime $faces(base)$file]
  519.     while {[gets $in input] >= 0} {
  520.         set parts [string tolower [split $input =]]
  521.         set lhs [string trim [lindex $parts 0]]
  522.         set rhs [split [string trim [lindex $parts 1]] /]
  523.         set faceMap($file,$lhs) $rhs
  524.     }
  525.     close $in
  526.     if [info exists faceMap($file,$item)] {
  527.         return $faceMap($file,$item)
  528.     }
  529.     }
  530.     return {}
  531. }
  532.  
  533. proc Face_FlushCache {} {
  534.     global faceMap faceCache
  535.     catch {unset faceMap}
  536.     catch {unset faceCache}
  537. }
  538.  
  539. #
  540. # Defer work to an after handler [this code should be elsewhere]
  541. #
  542.  
  543. proc DeferWork {name work {cancel {}}} {
  544.     upvar #0 $name queue
  545.  
  546.     lappend queue [list $work $cancel]
  547.     if {[llength $queue] == 1} {
  548.     after 50 DeferWorkProc $name
  549.     }
  550. }
  551. proc DeferWorkCancel name {
  552.     upvar #0 $name queue
  553.  
  554.     if [info exists queue] {
  555.     after cancel [list DeferWorkProc $name]
  556.     foreach w $queue {
  557.         catch [lindex $w 1]
  558.     }
  559.     unset queue
  560.     }
  561. }
  562. proc DeferWorkProc name {
  563.     upvar #0 $name queue
  564.  
  565.     set this [lindex $queue 0]
  566.     set queue [lrange $queue 1 end]
  567.     catch [lindex $this 0]
  568.     if [llength $queue] {
  569.     after 20 DeferWorkProc $name
  570.     }
  571. }
  572.